/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "imageknife_render.h"
#include "utils/log.h"
#include "imageknife_brush.h"
#include "common.h"
#include "native_drawing/drawing_image.h"
#include "utils/pixelmap_utils.h"
#include <native_drawing/drawing_bitmap.h>
#include <native_drawing/drawing_brush.h>
#include <native_drawing/drawing_canvas.h>
#include <native_drawing/drawing_color.h>
#include <native_drawing/drawing_color_filter.h>
#include <native_drawing/drawing_filter.h>
#include <native_drawing/drawing_font.h>
#include <native_drawing/drawing_path.h>
#include <native_drawing/drawing_pen.h>
#include <native_drawing/drawing_pixel_map.h>
#include <native_drawing/drawing_rect.h>
#include <native_drawing/drawing_round_rect.h>
#include <native_drawing/drawing_sampling_options.h>
#include <native_drawing/drawing_typeface.h>
#include <native_drawing/drawing_types.h>
#include "number_constant.h"

namespace ImageKnifePro {

static CalculatePosition UpdateContain(ArkUI_IntSize size, Image_Size imageSize)
{
    CalculatePosition position = {.left = 0, .top = 0, .right = size.width, .bottom = size.height};
    auto imageRatio = static_cast<float>(imageSize.width) / static_cast<float>(imageSize.height);
    auto canvasRatio = static_cast<float>(size.width) / static_cast<float>(size.height);
    const float NUM_2 = 2.0f;
    if (imageRatio > canvasRatio) {
        float newHeight = size.width / imageRatio;
        float top = (size.height - newHeight) / NUM_2;
        position.top = top;
        position.bottom = newHeight + top;
    } else {
        float newWidth = size.height * imageRatio;
        float left = (size.width - newWidth) / NUM_2;
        position.left = left;
        position.right = newWidth + left;
    }
    return position;
}

static CalculatePosition UpdateCover(ArkUI_IntSize size, Image_Size imageSize)
{
    CalculatePosition position = {.left = 0, .top = 0, .right = size.width, .bottom = size.height};
    auto imageRatio = static_cast<float>(imageSize.width) / static_cast<float>(imageSize.height);
    auto canvasRatio = static_cast<float>(size.width) / static_cast<float>(size.height);
    const float NUM_2 = 2.0f;
    bool useWidthScale = (imageRatio <= canvasRatio);
    float scale = useWidthScale ? static_cast<float>(size.width) / static_cast<float>(imageSize.width)
                                : static_cast<float>(size.height) / static_cast<float>(imageSize.height);
    if (useWidthScale) {
        float newHeight = static_cast<float>(imageSize.height) * scale;
        float top = abs(newHeight - size.height) / NUM_2;
        position.top = -top;
        position.bottom = newHeight - top;
    } else {
        float newWidth = static_cast<float>(imageSize.width) * scale;
        float left = abs(newWidth - size.width) / NUM_2;
        position.left = -left;
        position.right = newWidth - left;
    }
    return position;
}

static CalculatePosition UpdateNone(ArkUI_IntSize size, Image_Size imageSize)
{
    CalculatePosition position = {.left = 0, .top = 0, .right = size.width, .bottom = size.height};
    const float NUM_2 = 2.0f;
    position.left = (size.width - static_cast<float>(imageSize.width)) / NUM_2;
    position.top = (size.height - static_cast<float>(imageSize.height)) / NUM_2;
    position.bottom = position.top + imageSize.height;
    position.right = position.left + imageSize.width;
    return position;
}

static CalculatePosition UpdateFill(ArkUI_IntSize size, Image_Size imageSize)
{
    CalculatePosition position = {.left = 0, .top = 0, .right = size.width, .bottom = size.height};
    return position;
}

static std::map<int, std::function<CalculatePosition(ArkUI_IntSize size, Image_Size imageSize)>> g_hasMap = {
    {ARKUI_OBJECT_FIT_CONTAIN, [](ArkUI_IntSize size, Image_Size imagesize) { return UpdateContain(size, imagesize); }},
    {ARKUI_OBJECT_FIT_COVER, [](ArkUI_IntSize size, Image_Size imagesize) { return UpdateCover(size, imagesize); }},
    {ARKUI_OBJECT_FIT_NONE, [](ArkUI_IntSize size, Image_Size imagesize) { return UpdateNone(size, imagesize); }},
    {ARKUI_OBJECT_FIT_FILL, [](ArkUI_IntSize size, Image_Size imagesize) { return UpdateFill(size, imagesize); }},
};

void ImageKnifeRender::Clear(ArkUI_NodeCustomEvent *event)
{
    auto drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
    auto drawCanvas = reinterpret_cast<OH_Drawing_Canvas *>(OH_ArkUI_DrawContext_GetCanvas(drawContext));
    auto size = OH_ArkUI_DrawContext_GetSize(drawContext);
    IMAGE_KNIFE_CHECK(size.height == 0 || size.width == 0);
    IMAGE_KNIFE_LOG(LOG_DEBUG, "Canvas rect width: %{public}d, height: %{public}d", size.width, size.height);
    auto rectClip = OH_Drawing_RectCreate(0, 0, size.width, size.height);
    OH_Drawing_CanvasClipRect(drawCanvas, rectClip, INTERSECT, true);
    OH_Drawing_CanvasClear(drawCanvas, 0);
    // 释放资源
    OH_Drawing_CanvasDetachBrush(drawCanvas);
    OH_Drawing_RectDestroy(rectClip);
}

OH_Drawing_Rect *ImageKnifeRender::UpdateImageFit(DrawingArgs args, ArkUI_IntSize size, Image_Size imageSize,
                                                  float borderWidth)
{
    CalculatePosition position = {.left = 0, .top = 0, .right = size.width, .bottom = size.height};
    if (args.imageFit == ARKUI_OBJECT_FIT_SCALE_DOWN) {
        if (size.width < imageSize.width || size.height < imageSize.height) {
            args.imageFit = ARKUI_OBJECT_FIT_CONTAIN;
        } else {
            args.imageFit = ARKUI_OBJECT_FIT_NONE;
        }
    } else if (args.imageFit == ARKUI_OBJECT_FIT_AUTO) {
        auto imageRatio = static_cast<float>(imageSize.width) / static_cast<float>(imageSize.height);
        auto canvasRatio = static_cast<float>(size.width) / static_cast<float>(size.height);
        if (canvasRatio < imageRatio) {
            args.imageFit = ARKUI_OBJECT_FIT_CONTAIN;
        } else {
            args.imageFit = ARKUI_OBJECT_FIT_COVER;
        }
    }

    auto it = g_hasMap.find(args.imageFit);
    if (it != g_hasMap.end()) {
        position = it->second(size, imageSize);
    } else {
        IMAGE_KNIFE_LOG(LOG_DEBUG, "ImageKnifePro: UpdateImageFit Failed.");
    }

    // 采集OnComplete回调数据
    if (args.eventImage != nullptr) {
        args.eventImage->loadingStatus = 0;
        args.eventImage->width = imageSize.width;
        args.eventImage->height = imageSize.height;
        args.eventImage->componentWidth = size.width;
        args.eventImage->componentHeight = size.height;
        args.eventImage->contentOffsetX = position.left + borderWidth;
        args.eventImage->contentOffsetY = position.top + borderWidth;
        args.eventImage->contentWidth = position.right - borderWidth;
        args.eventImage->contentHeight = position.bottom - borderWidth;
    }
    return OH_Drawing_RectCreate(position.left + borderWidth, position.top + borderWidth, position.right - borderWidth,
                                 position.bottom - borderWidth);
}

void CanvasClipRountRect(OH_Drawing_Canvas *drawCanvas, const float &borderWidth, const ArkUI_IntSize &size,
                         const ImageKnifeRender::DrawingArgs &args)
{
    IMAGE_KNIFE_CHECK(drawCanvas == nullptr);
    auto displayDensity = GetDisplayDensity();
    OH_Drawing_RoundRect *roundClip = nullptr;
    auto rectClip =
        OH_Drawing_RectCreate(borderWidth, borderWidth, size.width - borderWidth, size.height - borderWidth);
    if (args.border != nullptr && args.border->radius.size() == NUM_1) {
        auto border = args.border->radius.front() * displayDensity - borderWidth;
        roundClip = OH_Drawing_RoundRectCreate(rectClip, border, border);
        OH_Drawing_CanvasClipRoundRect(drawCanvas, roundClip, INTERSECT, true);
    } else if (args.border != nullptr && args.border->radius.size() >= NUM_4) {
        roundClip = OH_Drawing_RoundRectCreate(rectClip, 0, 0);
        float topLeft = args.border->radius[NUM_0] * displayDensity - borderWidth;
        float topRight = args.border->radius[NUM_1] * displayDensity - borderWidth;
        float bottomLeft = args.border->radius[NUM_2] * displayDensity - borderWidth;
        float bottomRight = args.border->radius[NUM_3] * displayDensity - borderWidth;
        OH_Drawing_RoundRectSetCorner(roundClip, CORNER_POS_TOP_LEFT, {topLeft, topLeft});
        OH_Drawing_RoundRectSetCorner(roundClip, CORNER_POS_TOP_RIGHT, {topRight, topRight});
        OH_Drawing_RoundRectSetCorner(roundClip, CORNER_POS_BOTTOM_RIGHT, {bottomRight, bottomRight});
        OH_Drawing_RoundRectSetCorner(roundClip, CORNER_POS_BOTTOM_LEFT, {bottomLeft, bottomLeft});
        OH_Drawing_CanvasClipRoundRect(drawCanvas, roundClip, INTERSECT, true);
    } else {
        OH_Drawing_CanvasClipRect(drawCanvas, rectClip, INTERSECT, true);
    }

    OH_Drawing_RectDestroy(rectClip);
    if (roundClip != nullptr) {
        OH_Drawing_RoundRectDestroy(roundClip);
    }
}

void ImageKnifeRender::Drawing(ArkUI_NodeCustomEvent *event, OH_PixelmapNative *data, DrawingArgs args)
{
    static ImageKnifeBrush imageKnifeBrush;
    IMAGE_KNIFE_CHECK(data == nullptr);
    // left top right bottom
    Image_Size imageSize = PixelmapUtils::GetImageSize(data);
    IMAGE_KNIFE_CHECK(imageSize.height == 0 || imageSize.width == 0);
    IMAGE_KNIFE_LOG(LOG_DEBUG, "Src rect width: %{public}d, height: %{public}d", imageSize.width, imageSize.height);
    auto drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
    // 获取图形绘制对象。
    auto drawCanvas = reinterpret_cast<OH_Drawing_Canvas *>(OH_ArkUI_DrawContext_GetCanvas(drawContext));
    // 设置画布刷
    if (args.colorFilter != nullptr) {
        imageKnifeBrush.SetColorFilter(args.colorFilter);
        OH_Drawing_CanvasAttachBrush(drawCanvas, const_cast<OH_Drawing_Brush *>(imageKnifeBrush.GetCanvasBrush()));
    }
    // 获取组件大小。
    auto size = OH_ArkUI_DrawContext_GetSize(drawContext);
    IMAGE_KNIFE_CHECK(size.height == 0 || size.width == 0);
    IMAGE_KNIFE_LOG(LOG_DEBUG, "Canvas rect width: %{public}d, height: %{public}d", size.width, size.height);

    float borderWidth = 0;
    auto displayDensity = GetDisplayDensity();
    OH_Drawing_Rect *rectSrc = OH_Drawing_RectCreate(0, 0, imageSize.width, imageSize.height);
    if (args.border != nullptr && args.border->width.size() > 0) {
        borderWidth = args.border->width.front() * displayDensity;
    }

    CanvasClipRountRect(drawCanvas, borderWidth, size, args);

    auto rectCanvas = UpdateImageFit(args, size, imageSize, borderWidth);
    OH_Drawing_SamplingOptions *drawOption = OH_Drawing_SamplingOptionsCreate(FILTER_MODE_LINEAR, MIPMAP_MODE_NEAREST);
    OH_Drawing_PixelMap *drawingPixmap = OH_Drawing_PixelMapGetFromOhPixelMapNative(data);
    OH_Drawing_CanvasDrawPixelMapRect(drawCanvas, drawingPixmap, rectSrc, rectCanvas, drawOption);

    // 释放资源
    OH_Drawing_CanvasDetachBrush(drawCanvas);
    OH_Drawing_RectDestroy(rectSrc);
    OH_Drawing_RectDestroy(rectCanvas);
    OH_Drawing_PixelMapDissolve(drawingPixmap);
}

} // namespace ImageKnifePro
